#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <vector>
#include <list>
#include <mutex>
#include <memory>
#include <thread>
#include <condition_variable>
#include <atomic>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define     TEST_CONDITIONVAR               0


using namespace std;

/**                     条件变量 + 多线程 + 原子操作(atomic)
 *
 */
namespace ConditionVar
{
    class ConditionVariable
    {
    public:
        ConditionVariable()
        {
            m_flagAtomic.store(0);              // 初始化原子变量
            m_cntAtomic.store(0);
        }

        ~ConditionVariable()
        {
            std::cout << "~ConditionVariable" << endl;
            // 析构函数一定要线程, 否则报异常
            threadPtr->join();
            threadAddPtr->join();
            threadAdd1Ptr->join();
        }

        void startThread()
        {
            threadPtr.reset(new std::thread(std::bind(&ConditionVariable::threadFunc, this)));
            threadAddPtr.reset(new std::thread(std::bind(&ConditionVariable::threadFuncAdd, this)));
            threadAdd1Ptr.reset(new std::thread(std::bind(&ConditionVariable::threadFuncAdd1, this)));
        }

        void threadFunc()
        {
            std::cout << "ThreadFun start" << endl;
            while( !m_flagAtomic.load() )
            {
                std::cout << "Wait..." << endl;
                std::unique_lock<std::mutex> uniqueLck(m_mutex);
                m_condVar.wait(uniqueLck);
                std::cout << "Wait up!!!" << endl;

                //逻辑代码
            }
        }

        void threadFuncAdd()
        {
            for ( auto i=0; i<2; i++ )
            {
                std::cout << __PRETTY_FUNCTION__ << " |i: " << i << endl;
                m_cntAtomic += i;
            }
        }

        void threadFuncAdd1()
        {
            for ( auto i=2; i<4; i++ )
            {
                std::cout << __PRETTY_FUNCTION__ << " |i: " << i << endl;
                m_cntAtomic += i;
            }
        }

        void go()
        {
            std::unique_lock<std::mutex> uniqueLck(m_mutex);
            std::cout << "notify." << endl;
            m_flagAtomic.store(true);
            m_condVar.notify_all();
        }

        void printAtomic()
        {
            std::cout << "m_cntAtomic: " << m_cntAtomic << endl;
        }

    private:
        std::atomic<bool> m_flagAtomic;
        std::atomic<int> m_cntAtomic;
        unique_ptr<std::thread> threadPtr;
        unique_ptr<std::thread> threadAddPtr;
        unique_ptr<std::thread> threadAdd1Ptr;
        std::condition_variable m_condVar;
        std::mutex m_mutex;
    };
}


